package com.cwd.arm.domain.entity;

import com.alibaba.fastjson.JSONObject;
import com.cwd.arm.common.enums.ErrorEnum;
import com.cwd.arm.common.enums.MsgTypeEnum;
import com.cwd.arm.common.exception.ArmException;
import com.cwd.arm.common.util.AviatorEvaluatorUtil;
import com.cwd.arm.common.util.BigDecimalUtil;
import com.cwd.arm.common.util.DateUtil;
import com.cwd.arm.domain.entity.base.MsgReEntity;
import com.cwd.arm.domain.entity.vo.msg.PayWarnMsgVo;
import com.cwd.arm.domain.entity.vo.msg.RateWarnMsgVo;
import lombok.Data;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * Description:项目
 * Created on 2021年11月16日
 *
 * @author yan
 */
@Data
public class ProjectEntity implements MsgReEntity{

    /**
     *
     */
    private Long id;

    /**
     * 项目名称
     */
    private String projectName;

    /**
     * 甲方名称
     */
    private String partnerName;

    /**
     * 项目类型key
     */
    private String projectTypeKey;

    /**
     * 税点
     */
    private BigDecimal taxPoint;

    /**
     * 开工时间
     */
    private java.util.Date startDate;

    /**
     * 完工时间
     */
    private java.util.Date endDate;

    /**
     * 合同金额(万元)
     */
    private java.math.BigDecimal contractAmount;

    /**
     * 合同签订日期
     */
    private Date contractDate;

    /**
     * 应收金额
     */
    private java.math.BigDecimal receivableAmount;

    /**
     * 已收金额
     */
    private java.math.BigDecimal receivedAmount;


    /**
     * 结算金额
     */
    private java.math.BigDecimal closeAmount;

    /**
     * 结算时间
     */
    private Date closeDate;

    /**
     * 预计支出金额
     */
    private java.math.BigDecimal expectPayAmount;

    /**
     * 实际支出金额
     */
    private java.math.BigDecimal actualPayAmount;

    /**
     * 已开票未到账金额
     */
    private java.math.BigDecimal taxUnreceiveAmount;

    /**
     * 未到账金额
     */
    private java.math.BigDecimal unrevicedAmount;

    /**
     * 确认收入金额
     */
    private java.math.BigDecimal confirmAmount;

    /**
     *
     */
    private Boolean isBadDebt = false;

    /**
     * 收款周期
     */
    private Integer receiveCycle;

    /**
     * 10进行中;20结算中;30已结算;40待收尾款;50已完成收款
     */
    private Integer projectStatus;

    /**
     * 业务类型:normal常规;outside体外项目
     */
    private String businessType;

    /**
     * 备注
     */
    private String remark;

    /**
     * 收款负责人
     */
    private String receiveDirector;

    /**
     * 资料负责人
     */
    private String dataDirector;

    private BigDecimal nodeCloseAmount;

    private BigDecimal cashRate;

    /**
     * 记录节点的变化
     */
    private List<ReceiveNodeEntity> diffNodeEntityList;

    /**
     * 记录确认收入的变化
     */
    private List<ConfirmIncomeEntity> diffConfirmIncomeEntityList;

    /**
     * 记录支出的变化
     */
    private List<PayEntity> diffPayEntityList;


    /**
     * 是否需要财务角色
     *
     * @return
     */
    public boolean needFinRole(BigDecimal closeAmount) {
        if (!BigDecimalUtil.hasRecord(closeAmount)) {
            return false;
        }
        if (BigDecimalUtil.isEqual(this.closeAmount, closeAmount)) {
            return false;
        }
        return true;
    }

    /**
     * 是否可以删除
     *
     * @return
     */
    public boolean canDel() {
        //有收款节点就不能删除
        if (BigDecimalUtil.hasRecord(receivableAmount)) {
            throw new ArmException(ErrorEnum.VAILD_ERROR, "项目已有收款节点,无法删除");
        }
        if (BigDecimalUtil.hasRecord(confirmAmount)) {
            throw new ArmException(ErrorEnum.VAILD_ERROR, "项目已有确认收入,无法删除");
        }
        if (BigDecimalUtil.hasRecord(actualPayAmount)) {
            throw new ArmException(ErrorEnum.VAILD_ERROR, "项目已有支出记录,无法删除");
        }
        if (BigDecimalUtil.hasRecord(expectPayAmount)) {
            throw new ArmException(ErrorEnum.VAILD_ERROR, "项目已有支出记录,无法删除");
        }
        return true;
    }

    //------------------------------->收款节点
    public void addNode(List<ReceiveNodeEntity> nodeEntityList) {
        this.diffNodeEntityList = nodeEntityList;
        calAmountByNode();
    }

    public void updateNode(ReceiveNodeEntity oldNodeEntity, ReceiveNodeEntity newNodeEntity) {
        newNodeEntity.initAmount(oldNodeEntity);
        newNodeEntity.initEndDate(oldNodeEntity);
        oldNodeEntity.undo();
        diffNodeEntityList = new ArrayList<>(2);
        diffNodeEntityList.add(newNodeEntity);
        diffNodeEntityList.add(oldNodeEntity);
        calAmountByNode();
    }

    public void delNode(ReceiveNodeEntity nodeEntity) {
        nodeEntity.undo();
        diffNodeEntityList = Arrays.asList(nodeEntity);
        calAmountByNode();
    }

    public void calUnrevicedAmount() {
        if (BigDecimalUtil.hasRecord(closeAmount)) {
            unrevicedAmount = BigDecimalUtil.sub(closeAmount, receivedAmount);
        } else {
            unrevicedAmount = BigDecimalUtil.sub(receivableAmount, receivedAmount);
        }
    }

    /**
     * 因为节点信息发生变化,而导致的项目每月信息发生的变化
     *
     * @return
     */
    public List<ProjectMonthAmountTotalEntity> getDiffMonthAmountByDiffNode() {
        List<ProjectMonthAmountTotalEntity> monthAmountEntityList = new ArrayList<>();
        diffNodeEntityList.stream().forEach(
                nodeEntity -> {
                    setNodeMonthAmount(monthAmountEntityList, nodeEntity);
                    setReceivedMonthAmount(monthAmountEntityList, nodeEntity);
                }
        );
        return monthAmountEntityList;
    }

    /**
     * 由与收款节点变化重新统计项目金额
     */
    private void calAmountByNode() {
        receivableAmount = BigDecimalUtil.add(receivableAmount, getAddReceivableAmount());
        receivedAmount = BigDecimalUtil.add(receivedAmount, getAddReceivedAmount());
        taxUnreceiveAmount = BigDecimalUtil.add(taxUnreceiveAmount, getAddTaxUnreceiveAmount());
        nodeCloseAmount = BigDecimalUtil.add(nodeCloseAmount, getAddNodeCloseAmount());
    }

    //----------------------->确认收入
    public void addConfirmIncome(ConfirmIncomeEntity newConfirmIncomeEntity) {
        this.diffConfirmIncomeEntityList = new ArrayList<>(1);
        diffConfirmIncomeEntityList.add(newConfirmIncomeEntity);
        calAmountByConfirmAmount();
    }

    public void updateConfirmIncome(ConfirmIncomeEntity oldConfirmIncomeEntity, ConfirmIncomeEntity newConfirmIncomeEntity) {
        newConfirmIncomeEntity.initAmount(oldConfirmIncomeEntity);
        oldConfirmIncomeEntity.undo();
        this.diffConfirmIncomeEntityList = new ArrayList<>(2);
        diffConfirmIncomeEntityList.add(newConfirmIncomeEntity);
        diffConfirmIncomeEntityList.add(oldConfirmIncomeEntity);
        calAmountByConfirmAmount();
    }

    public void delConfirmIncome(ConfirmIncomeEntity confirmIncomeEntity) {
        confirmIncomeEntity.undo();
        diffConfirmIncomeEntityList = Arrays.asList(confirmIncomeEntity);
        calAmountByConfirmAmount();
    }

    public List<ProjectMonthAmountTotalEntity> getDiffMonthAmountByDiffConfirmIncome() {
        List<ProjectMonthAmountTotalEntity> monthAmountEntityList = new ArrayList<>();
        diffConfirmIncomeEntityList.stream().filter(confirmIncomeEntity -> confirmIncomeEntity.getHasReceipt()).forEach(
                confirmIncomeEntity -> {
                    setConfirmMonthAmount(monthAmountEntityList, confirmIncomeEntity);
                }
        );
        return monthAmountEntityList;
    }

    private void calAmountByConfirmAmount() {
        confirmAmount = confirmAmount.add(getAddConfirmAmount());
    }


    //----------------------------->支出
    public void addPay(PayEntity payEntity) {
        this.diffPayEntityList = new ArrayList<>(1);
        diffPayEntityList.add(payEntity);
        calAmountByPay();
    }

    public void updatePay(PayEntity oldPayEntity, PayEntity newPayEntity) {
        newPayEntity.initAmount(oldPayEntity);
        oldPayEntity.undo();
        this.diffPayEntityList = new ArrayList<>(2);
        diffPayEntityList.add(newPayEntity);
        diffPayEntityList.add(oldPayEntity);
        calAmountByPay();
    }

    public void delPay(PayEntity payEntity) {
        payEntity.undo();
        diffPayEntityList = Arrays.asList(payEntity);
        calAmountByPay();
    }

    public List<ProjectMonthAmountTotalEntity> getDiffMonthAmountByDiffPay() {
        List<ProjectMonthAmountTotalEntity> monthAmountEntityList = new ArrayList<>();
        diffPayEntityList.stream().forEach(
                payEntity -> {
                    setPayMonthAmount(monthAmountEntityList, payEntity);
                }
        );
        return monthAmountEntityList;
    }

    public BigDecimal getCalAmount(){
        if(BigDecimalUtil.hasRecord(closeAmount)){
            return closeAmount;
        }
        return contractAmount;
    }

    @Override
    public boolean isMatch(MsgTypeEnum msgTypeEnum) {
        if(msgTypeEnum == MsgTypeEnum.RATE_WARN){
            if (AviatorEvaluatorUtil.grossRateWarn(actualPayAmount, contractAmount)) {
                return true;
            }
        }else if(msgTypeEnum == MsgTypeEnum.PAY_WARN){
            if (AviatorEvaluatorUtil.grossPayWarn(actualPayAmount, receivedAmount,cashRate)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public String getContent(MsgTypeEnum msgTypeEnum) {
        if(msgTypeEnum == MsgTypeEnum.RATE_WARN){
          return "项目'"+projectName+"'的支出大于合同金额的75%";
        }
        if(msgTypeEnum == MsgTypeEnum.PAY_WARN){
            return "项目'"+projectName+"'的支出收入比大于"+BigDecimalUtil.convertToRate(cashRate);
        }
        return "";
    }

    @Override
    public String getMsgParam(MsgTypeEnum msgTypeEnum) {
        if(msgTypeEnum == MsgTypeEnum.RATE_WARN){
            return JSONObject.toJSONString(new RateWarnMsgVo(projectName));
        }
        if(msgTypeEnum == MsgTypeEnum.PAY_WARN){
            return JSONObject.toJSONString(new PayWarnMsgVo(projectName,BigDecimalUtil.convertToRate(cashRate)));
        }
        return "";
    }

    private void calAmountByPay() {
        actualPayAmount = actualPayAmount.add(getAddPayActualAmount());
        expectPayAmount = expectPayAmount.add(getAddPayExpectAmount());
    }


    private void setNodeMonthAmount(List<ProjectMonthAmountTotalEntity> monthAmountEntityList, ReceiveNodeEntity nodeEntity) {
        ProjectMonthAmountTotalEntity projectMonthAmountTotalEntity = getProjectMonthAmountTotalEntity(monthAmountEntityList, nodeEntity.getEndDate());
        if (projectMonthAmountTotalEntity == null) {
            monthAmountEntityList.add(buildMonthAmountEntityByAddNode(nodeEntity));
        } else {
            projectMonthAmountTotalEntity.addReceivableAmount(nodeEntity.getReceivableAmount());
//            projectMonthAmountTotalEntity.addNodeReceivedAmount(nodeEntity.getReceivedAmount());
        }
    }

    private void setReceivedMonthAmount(List<ProjectMonthAmountTotalEntity> monthAmountEntityList, ReceiveNodeEntity nodeEntity) {
        List<TaxEntity> taxEntityList = nodeEntity.getDiffTaxEntityList();
        for (TaxEntity taxEntity : taxEntityList) {
            List<ReceiveRecordEntity> receiveRecordEntityList = taxEntity.getDiffReceiveRecordEntityList();
            for (ReceiveRecordEntity receiveRecordEntity : receiveRecordEntityList) {
                ProjectMonthAmountTotalEntity projectMonthAmountTotalEntity = getProjectMonthAmountTotalEntity(monthAmountEntityList, receiveRecordEntity.getReceiveDate());
                if (projectMonthAmountTotalEntity == null) {
                    monthAmountEntityList.add(buildMonthAmountEntityByAddRevived(receiveRecordEntity));
                } else {
                    projectMonthAmountTotalEntity.addReceivedAmount(receiveRecordEntity.getAmount());
                }
            }
        }

    }

    private void setConfirmMonthAmount(List<ProjectMonthAmountTotalEntity> monthAmountEntityList, ConfirmIncomeEntity confirmIncomeEntity) {
        ProjectMonthAmountTotalEntity projectMonthAmountTotalEntity = getProjectMonthAmountTotalEntity(monthAmountEntityList, confirmIncomeEntity.getConfirmDate());
        if (projectMonthAmountTotalEntity == null) {
            monthAmountEntityList.add(buildMonthAmountEntityByConfirm(confirmIncomeEntity));
        } else {
            projectMonthAmountTotalEntity.addConfirmAmount(confirmIncomeEntity.getConfirmAmount());
        }
    }

    private void setPayMonthAmount(List<ProjectMonthAmountTotalEntity> monthAmountEntityList, PayEntity payEntity) {
        ProjectMonthAmountTotalEntity projectMonthAmountTotalEntity = getProjectMonthAmountTotalEntity(monthAmountEntityList, payEntity.getPayDate());
        if (projectMonthAmountTotalEntity == null) {
            monthAmountEntityList.add(buildMonthAmountEntityByPay(payEntity));
        } else {
            projectMonthAmountTotalEntity.addExpectPayAmount(payEntity.getExpectAmount());
            projectMonthAmountTotalEntity.addActualPayAmount(payEntity.getActualAmount());
        }
    }


    private ProjectMonthAmountTotalEntity getProjectMonthAmountTotalEntity(List<ProjectMonthAmountTotalEntity> monthAmountEntityList, Date date) {
        int year = DateUtil.getYear(date);
        int month = DateUtil.getMonth(date);
        return monthAmountEntityList.stream()
                .filter(entity -> (month == entity.getFyMonth()) && year == entity.getFyYear()).findAny().orElse(null);
    }

    private ProjectMonthAmountTotalEntity buildMonthAmountEntityByAddNode(ReceiveNodeEntity nodeEntity) {
        int year = DateUtil.getYear(nodeEntity.getEndDate());
        int month = DateUtil.getMonth(nodeEntity.getEndDate());
        ProjectMonthAmountTotalEntity entity = new ProjectMonthAmountTotalEntity();
        entity.setProjectId(id);
        entity.setFyYear(year);
        entity.setFyMonth(month);
        entity.setReceivableAmount(nodeEntity.getReceivableAmount());
//        entity.setReceivedAmount(nodeEntity.getReceivedAmount());
        return entity;
    }

    private ProjectMonthAmountTotalEntity buildMonthAmountEntityByAddRevived(ReceiveRecordEntity receiveRecordEntity) {
        int year = DateUtil.getYear(receiveRecordEntity.getReceiveDate());
        int month = DateUtil.getMonth(receiveRecordEntity.getReceiveDate());
        ProjectMonthAmountTotalEntity entity = new ProjectMonthAmountTotalEntity();
        entity.setProjectId(id);
        entity.setFyYear(year);
        entity.setFyMonth(month);
        entity.setReceivedAmount(receiveRecordEntity.getAmount());
        return entity;
    }

    private ProjectMonthAmountTotalEntity buildMonthAmountEntityByConfirm(ConfirmIncomeEntity confirmIncomeEntity) {
        int year = DateUtil.getYear(confirmIncomeEntity.getConfirmDate());
        int month = DateUtil.getMonth(confirmIncomeEntity.getConfirmDate());
        ProjectMonthAmountTotalEntity entity = new ProjectMonthAmountTotalEntity();
        entity.setProjectId(id);
        entity.setFyYear(year);
        entity.setFyMonth(month);
        entity.setConfirmAmount(confirmIncomeEntity.getConfirmAmount());
        return entity;
    }

    private ProjectMonthAmountTotalEntity buildMonthAmountEntityByPay(PayEntity payEntity) {
        int year = DateUtil.getYear(payEntity.getPayDate());
        int month = DateUtil.getMonth(payEntity.getPayDate());
        ProjectMonthAmountTotalEntity entity = new ProjectMonthAmountTotalEntity();
        entity.setProjectId(id);
        entity.setFyYear(year);
        entity.setFyMonth(month);
        entity.setActualPayAmount(payEntity.getActualAmount());
        entity.setExpectPayAmount(payEntity.getExpectAmount());
        return entity;
    }


    private BigDecimal getAddReceivableAmount() {
        return diffNodeEntityList.stream().filter(nodeEntity -> (nodeEntity.getReceivableAmount() != null))
                .map(ReceiveNodeEntity::getReceivableAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private BigDecimal getAddNodeCloseAmount() {
        return diffNodeEntityList.stream().filter(nodeEntity -> (nodeEntity.getCloseAmount() != null))
                .map(ReceiveNodeEntity::getCloseAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private BigDecimal getAddReceivedAmount() {
        return diffNodeEntityList.stream()
                .filter(nodeEntity -> (nodeEntity.getReceivedAmount() != null))
                .map(ReceiveNodeEntity::getReceivedAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private BigDecimal getAddTaxUnreceiveAmount() {
        return diffNodeEntityList.stream()
                .map(ReceiveNodeEntity::getTaxUnreceiveAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private BigDecimal getAddConfirmAmount() {
        return diffConfirmIncomeEntityList.stream()
                .filter(confirmIncomeEntity -> (confirmIncomeEntity.getConfirmAmount() != null && confirmIncomeEntity.getHasReceipt()))
                .map(ConfirmIncomeEntity::getConfirmAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private BigDecimal getAddPayActualAmount() {
        return diffPayEntityList.stream().filter(payEntity -> (payEntity.getActualAmount() != null))
                .map(PayEntity::getActualAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private BigDecimal getAddPayExpectAmount() {
        return diffPayEntityList.stream().filter(payEntity -> (payEntity.getExpectAmount() != null))
                .map(PayEntity::getExpectAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }


}
